package com.fable.sp.core.controller;

import com.fable.sp.core.domain.*;
import com.fable.sp.core.enums.ColumnType;
import com.fable.sp.core.service.LayoutQuery;
import com.fable.sp.core.service.WidgetService;
import com.fable.sp.core.support.GridStackIE8CSSCreator;
import com.fable.sp.core.template.TemplateNotFoundException;
import com.fable.sp.core.template.TemplateRenderException;
import com.fable.sp.core.util.JSONUtil;
import com.fable.sp.core.widget.*;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("/sp")
public class SmartPageController {

    private static final Logger LOGGER = LoggerFactory.getLogger(SmartPageController.class);

    @Autowired
    private WidgetService widgetService;

    @RequestMapping("/manager")
    public void manager() {
    }

    /**********************
     * 需要wrapper
     ******************/


    @RequestMapping(value = "/containers", method = RequestMethod.GET)
    public void containers(ContainerQuery query, Pageable pageable, HttpServletResponse response) throws Exception {
        Map<String, Object> args = Maps.newHashMap();
        args.put("page", widgetService.queryContainers(query, pageable));
        widgetService.renderAndWrap("containers", args, response);
    }

    @RequestMapping(value = "/addContainer", method = RequestMethod.GET)
    public void addContainer(Long containerId, HttpServletResponse response) throws Exception {
        Map<String, Object> args = Maps.newHashMap();
        if (containerId != null) {
            args.put("container", widgetService.getContainer(containerId));
        }
        setupWizardAttribute(containerId, args);
        widgetService.renderAndWrap("addContainer", args, response);
    }

    @RequestMapping(value = "/addLayout", method = RequestMethod.GET)
    public void addLayout(Long containerId, HttpServletResponse response) throws Exception {
        Map<String, Object> args = Maps.newHashMap();
        if (containerId != null) {
            Layout layout = widgetService.getOriginalLayout(containerId);
            if (layout == null) {
                layout = widgetService.createOriginalLayout(containerId);
            }
            List<LayoutItem> layoutItems = widgetService.getLayoutItems(layout.getId());
            args.put("layoutItems", layoutItems);
            args.put("layoutItemsJson", JSONUtil.toCompatibleJSON(layoutItems));
            args.put("layout", layout);
            setupWizardAttribute(containerId, args);
        }
        widgetService.renderAndWrap("addLayout", args, response);
    }

    @RequestMapping(value = "/addWidget", method = RequestMethod.GET)
    public void addWidget(Long containerId, HttpServletRequest request, HttpServletResponse response) throws Exception {
        Map<String, Object> args = Maps.newHashMap();
        if (containerId != null) {
            Layout layout = widgetService.getOriginalLayout(containerId);
            args.put("layoutItems", widgetService.getLayoutItems(layout.getId()));
            args.put("renderedLayout", widgetService.renderContainer(containerId, request, null));
            setupWizardAttribute(containerId, args);
        }
        widgetService.renderAndWrap("addWidget", args, response);
    }

    @RequestMapping("/dataBinding")
    public void dataBinding(Long containerId, HttpServletResponse response) throws Exception {
        Map<String, Object> args = Maps.newHashMap();
        List<WidgetInstance> instances = widgetService.getContainerWidgetInstances(containerId);
        List<WidgetInstance> pInstances = Lists.newArrayList();
        Map<Long, List<ColumnType>> insSupportTypes = Maps.newHashMap();
        for (WidgetInstance instance : instances) {
            Widget widget = widgetService.getWidget(instance.getWidgetName());
            if (widget instanceof Persistentable) {
                Persistentable pfs = (Persistentable) widget;
                PersistentField persistentField = pfs.defineField();
                List<ColumnType> supportTypes;
                if (persistentField != null && !CollectionUtils.isEmpty(supportTypes = persistentField.getSupportTypes())) {
                    insSupportTypes.put(instance.getId(), supportTypes);
                    pInstances.add(instance);
                }
            }
        }
        args.put("insSupportTypes", insSupportTypes);
        args.put("pInstances", pInstances);
        args.put("containerTable", widgetService.getContainerTable(containerId));
        setupWizardAttribute(containerId, args);
        widgetService.renderAndWrap("dataBinding", args, response);
    }

    @RequestMapping("/preview")
    public void preview(Long layoutId, Long recordId, HttpServletRequest request, HttpServletResponse response) throws Exception {
        Map<String, Object> args = Maps.newHashMap();
        args.put("rendered", widgetService.renderLayout(layoutId, recordId, request, null));
        widgetService.renderAndWrap("preview", args, response);
    }

    @RequestMapping("/renderLayout")
    public void renderLayout(Long layoutId, Long recordId, HttpServletRequest request, HttpServletResponse response) throws Exception {
        Map<String, Object> args = Maps.newHashMap();
        args.put("rendered", widgetService.renderLayout(layoutId, recordId, request, null));
        widgetService.renderAndWrap("renderLayout", args, response);
    }

    @RequestMapping("/renderLayoutItem")
    @ResponseBody
    public String renderLayoutItem(Long layoutItemId, HttpServletRequest request) throws Exception {
        return widgetService.renderLayoutItem(layoutItemId, request, null);
    }

    @RequestMapping("/renderLayoutItemContent")
    @ResponseBody
    public String renderLayoutItemContent(Long id, HttpServletRequest request) throws Exception {
        return widgetService.renderLayoutItemContent(id, request, null);
    }

    @RequestMapping(value = "/layoutControlInfo", method = RequestMethod.GET)
    public void layoutControlInfo(Long layoutId, HttpServletResponse response) throws Exception {
        Map<String, Object> args = Maps.newHashMap();
        args.put("layout", widgetService.getLayout(layoutId));
        widgetService.renderAndWrap("layoutControlInfo", args, response);
    }

    @RequestMapping(value = "/layoutControl", method = RequestMethod.GET)
    public void layoutControl(Long layoutId, HttpServletResponse response) throws Exception {
        Map<String, Object> args = Maps.newHashMap();
        List<LayoutItem> layoutItems = widgetService.getLayoutItems(layoutId);
        args.put("layoutItems", layoutItems);
        args.put("layoutItemsJson", JSONUtil.toCompatibleJSON(layoutItems));
        args.put("layout", widgetService.getLayout(layoutId));
        widgetService.renderAndWrap("layoutControl", args, response);
    }

    @RequestMapping(value = "/widgetControl", method = RequestMethod.GET)
    public void widgetControl(Long layoutId, HttpServletRequest request, HttpServletResponse response) throws Exception {
        Map<String, Object> args = Maps.newHashMap();
        args.put("layoutItems", JSONUtil.toCompatibleJSON(widgetService.getLayoutItems(layoutId)));
        args.put("layout", widgetService.getLayout(layoutId));
        args.put("renderedLayout", widgetService.renderLayout(layoutId, request, null));
        widgetService.renderAndWrap("widgetControl", args, response);
    }

    /*********************************************************************/
    @RequestMapping(value = "/containerSelect", method = RequestMethod.GET)
    public void containerSelect(ContainerQuery query, Pageable pageable, ModelMap modelMap) throws TemplateRenderException, TemplateNotFoundException {
        modelMap.put("page", widgetService.queryContainers(query, pageable));
    }

    @RequestMapping(value = "/addContainer", method = RequestMethod.POST)
    public String addContainer(Container container) {
        widgetService.saveContainer(container);
        return "redirect:/sp/addLayout?containerId=" + container.getId();
    }

    @RequestMapping(value = "/addLayout", method = RequestMethod.POST)
    public String addLayout(Layout layout) {
        widgetService.saveLayout(layout);
        return "redirect:/sp/addWidget?containerId=" + layout.getContainerId();
    }

    @RequestMapping(value = "/saveLayoutItems", method = RequestMethod.POST)
    @ResponseBody
    public List<LayoutItem> saveLayoutItems(@RequestBody List<LayoutItem> layoutItems) {
        return widgetService.saveLayoutItems(layoutItems);
    }

    @RequestMapping(value = "/removeLayoutItem", method = RequestMethod.POST)
    @ResponseStatus(HttpStatus.OK)
    public void removeLayoutItem(Long id) {
        widgetService.removeLayoutItem(id);
    }

    @RequestMapping(value = "/removeContent", method = RequestMethod.POST)
    @ResponseStatus(HttpStatus.OK)
    public void removeContent(Long id) {
        widgetService.removeLayoutItemContent(id);
    }

    @RequestMapping(value = "/editLayout", method = RequestMethod.GET)
    public void editLayout(Long id, ModelMap modelMap) {
        modelMap.put("layout", widgetService.getLayout(id));
    }

    @RequestMapping(value = "/editLayoutItem", method = RequestMethod.GET)
    public void editLayoutItem(Long id, ModelMap modelMap) {
        modelMap.put("layoutItem", widgetService.getLayoutItem(id));
    }

    @RequestMapping(value = "/editContent", method = RequestMethod.GET)
    public void editContent(Long id, ModelMap modelMap) {
        LayoutItemContent content = widgetService.getLayoutItemContent(id);
        WidgetInstance instance = widgetService.getWidgetInstance(content.getContentId());
        String widgetName = instance.getWidgetName();
        Widget widget = widgetService.getWidget(widgetName);
        if (widget instanceof Persistentable) {
            modelMap.put("switchable", true);
        }
        if (widget instanceof ConfigurableWidget) {
            modelMap.put("hasPreference", !CollectionUtils.isEmpty(((ConfigurableWidget) widget).getPreferences()));
        }
        modelMap.put("instance", instance);
        modelMap.put("content", content);
    }

    @RequestMapping(value = "/saveGlobalLayoutItemStyle", method = RequestMethod.POST)
    @ResponseBody
    public String saveGlobalLayoutItemStyle(Layout layout) {
        return widgetService.saveGlobalLayoutItemStyle(layout).getStyle().toString();
    }

    @RequestMapping(value = "/addWidget", method = RequestMethod.POST)
    @ResponseBody
    public LayoutItemContent addWidget(String widgetName, Long itemId) {
        return widgetService.addWidget(widgetName, itemId);
    }

    @RequestMapping(value = "/saveContentMode", method = RequestMethod.POST)
    @ResponseStatus(HttpStatus.OK)
    public void saveContentMode(Long contentId, String mode) {
        widgetService.saveContentMode(contentId, mode);
    }

    @RequestMapping(value = "/widgets", method = RequestMethod.GET)
    public void widgets(WidgetQuery query, Long layoutId, boolean instance, ModelMap modelMap) {
        if (instance) {
            modelMap.put("configs", widgetService.getWidgetConfigs());
            modelMap.put("unusedContents", widgetService.getUnusedContents(layoutId));
        } else {
            Map<String, Integer> tagCountMap = widgetService.getWidgetTagMap(null);
            if (query == null) {
                query = new WidgetQuery();
            }
            modelMap.put("tagCountMap", tagCountMap);
            modelMap.put("widgets", widgetService.queryWidgets(query));
            modelMap.put("total", widgetService.queryWidgets(null).size());
        }
    }

    @RequestMapping(value = "/updateLayoutItemContents", method = RequestMethod.POST)
    @ResponseBody
    public void updateLayoutItemContents(@RequestBody List<LayoutItemContent> itemContents) {
        widgetService.updateLayoutItemContents(itemContents);
    }

    public void setupWizardAttribute(Long containerId, Map<String, Object> modelMap) {
        modelMap.put("containerId", containerId);
        Layout layout = (Layout) modelMap.get("layout");
        if (layout == null) {
            layout = widgetService.getOriginalLayout(containerId);
            modelMap.put("layout", layout);
        }
        if (layout != null) {
            modelMap.put("hasWidget", widgetService.getLayoutItemContents(layout.getId()).size() > 0);
        }
    }

    @RequestMapping("/saveContainerTable")
    @ResponseBody
    public boolean saveContainerTable(@RequestBody Map<Long, ColumnDef> instanceColumns, @RequestParam Long containerId) {
        return widgetService.saveContainerTable(containerId, instanceColumns);
    }

    @RequestMapping("/renderContainer")
    public void renderContainer(Long containerId, HttpServletRequest request, HttpServletResponse response) {
        try {
            response.getWriter().print(widgetService.renderContainer(containerId, request, response));
        } catch (Exception e) {
            LOGGER.error("Render container error", e);
        }
    }

    @RequestMapping("/renderWidget")
    public void renderWidget(Long instanceId, HttpServletRequest request, HttpServletResponse response) {
        try {
            response.getWriter().print(widgetService.renderWidget(instanceId, request, response));
        } catch (IOException e) {
            LOGGER.error("Render widget error", e);
        }
    }

    @RequestMapping("/layouts")
    public void layouts(LayoutQuery layoutQuery, ModelMap modelMap) {
        modelMap.put("layouts", widgetService.listLayouts(layoutQuery));
    }

    @RequestMapping(value = "/layoutInfo", method = RequestMethod.GET)
    public void layoutInfo(Long containerId, Long id, boolean copy, ModelMap modelMap) {
        if (id == null) {
            modelMap.put("containerId", containerId);
        } else {
            Layout layout = widgetService.getLayout(id);
            modelMap.put("containerId", layout.getContainerId());
            modelMap.put("layout", layout);
            modelMap.put("copy", copy);
        }
    }

    @RequestMapping(value = "/layoutControl", method = RequestMethod.POST)
    @ResponseBody
    public Layout layoutControl(Layout layout) {
        return widgetService.saveLayoutControl(layout);
    }


    @RequestMapping(value = "/layoutControlInfo", method = RequestMethod.POST)
    public String layoutControlInfo(Layout layout) {
        Layout lay = widgetService.saveLayoutControlInfo(layout);
        return "redirect:/sp/layoutControl?layoutId=" + lay.getId();
    }

    @RequestMapping(value = "/saveContent", method = RequestMethod.POST)
    @ResponseBody
    public LayoutItemContent saveContent(LayoutItemContent content) {
        return widgetService.saveLayoutItemContent(content);
    }

    @RequestMapping(value = "/saveContentStyle", method = RequestMethod.POST)
    @ResponseBody
    public String saveContentStyle(LayoutItemContent content) {
        LayoutItemContent lic = widgetService.saveLayoutItemContentStyle(content);
        return lic.getStyle().toString();
    }

    @RequestMapping(value = "/saveLayoutItemStyle", method = RequestMethod.POST)
    @ResponseBody
    public String saveItemStyle(LayoutItem layoutItem) {
        LayoutItem saved = widgetService.saveLayoutItemStyle(layoutItem);
        return saved.getStyle().toString();
    }

    @RequestMapping(value = "/saveLayoutStyle", method = RequestMethod.POST)
    @ResponseBody
    public String saveLayoutStyle(Layout layout) {
        Layout lo = widgetService.saveLayoutStyle(layout);
        return lo.getStyle().toString();
    }

    @RequestMapping(value = "/ie8Css.css")
    @ResponseBody
    public String ie8Css(Integer height) {
        height = height == null ? 60 : height;
        return GridStackIE8CSSCreator.gen(height, 0);
    }

    @RequestMapping("/widgetPreference")
    public String widgetPreference(Long widgetId, HttpServletRequest request, ModelMap modelMap) {
        modelMap.put("rendered", widgetService.renderWidgetPreference(widgetId, request));
        return "/global/" + WidgetRenderer.PREFER_TEMPLATE;
    }

    @RequestMapping(value = "/saveWidgetPreferences", method = RequestMethod.POST)
    @ResponseStatus(HttpStatus.OK)
    public void saveWidgetPreferences(Long widgetId, @RequestBody List<Preference> preferences) {
        widgetService.saveWidgetPreferences(widgetId, preferences);
    }

    @RequestMapping(value = "/saveRecord", method = RequestMethod.POST)
    @ResponseBody
    public Long saveRecord(Long layoutId, Long recordId, @RequestBody Map<Long, String> fieldValues) {
        Long rid = widgetService.saveRecord(layoutId, recordId, fieldValues);
        return rid == null ? 0 : rid;
    }

    @RequestMapping(value = "/layoutFields", method = RequestMethod.GET)
    public void layoutFields(Long layoutId,boolean simple, ModelMap modelMap) {
        modelMap.put("layoutFields", widgetService.getLayoutFields(layoutId,simple));
    }

    @RequestMapping(value = "/fakeDelete", method = RequestMethod.POST)
    @ResponseStatus(HttpStatus.OK)
    public void fakeDelete(String className, Long id) {
        widgetService.fakeDelete(className, id);
    }

    @RequestMapping(value = "/saveInstance", method = RequestMethod.POST)
    @ResponseStatus(HttpStatus.OK)
    public void saveInstance(Long id, String info) {
        widgetService.saveWidgetInstanceInfo(id, info);
    }

    @RequestMapping(value = "/layout", method = RequestMethod.GET)
    public void layout(Long layoutId, HttpServletResponse response) throws TemplateRenderException, IOException, TemplateNotFoundException {
        Map<String, Object> args = Maps.newHashMap();
        bindWizard(layoutId, args);
        widgetService.renderAndWrap("layout", args, response);
    }

    @RequestMapping(value = "/layoutItems", method = RequestMethod.GET)
    public void layoutItems(Long layoutId, HttpServletResponse response) throws TemplateRenderException, IOException, TemplateNotFoundException {
        Map<String, Object> args = Maps.newHashMap();
        bindWizard(layoutId, args);
        widgetService.renderAndWrap("layout", args, response);
    }

    public void bindWizard(Long layoutId, Map<String, Object> args) {
        if (layoutId != null) {
            Layout layout = widgetService.getLayout(layoutId);
            Container container = widgetService.getContainer(layout.getContainerId());
            args.put("layout", layout);
            args.put("layoutId", layoutId);
            args.put("container", container);
            args.put("containerId", container.getId());
            args.put("itemContents", widgetService.getLayoutItemContents(layoutId));
        }
    }
}
